package org.ashleygwinnell.snahs;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.net.SocketException;
import java.util.StringTokenizer;

import org.ashleygwinnell.snahs.err.RequestNotSupportedException;

/**
 * This class handles HTTP requests sent to the server
 * @author Ashley Gwinnell
 */
public class ThreadedRequest implements Runnable
{
	/** The characters for a new line */
	public static final String CRLF = "\r\n";
	
	/** The server teh the socks belongs to */
	private SimpleHTTPServer server;
	
	/** The Socket that the request is being handled at. */
	private Socket socket;
	
	/** The InputStream of the socket */
	private InputStream inputStream;
	
	/** The OutputStream of the socket */
	private OutputStream outputStream;
	
	/** To read the request */
	private BufferedReader br;
	
	private String headerLine;
	
	private Thread thread;
	
	/**
	 * Handle the HTTP Request.
	 * @param socket - the socket where the request is being received.
	 * @throws IOException Thrown when getting the Sockets input and output streams.
	 */
	public ThreadedRequest(Socket socket) throws IOException
	{
		this.socket = socket;
		this.inputStream = socket.getInputStream();
		this.outputStream = socket.getOutputStream();
		
		this.thread = new Thread(this);
	}
	
	/**
	 * This method closes all of the open connections/sockets/ports.
	 */
	public final void close()
    {
		try 
		{
			System.out.println("Connection Closed.\n");
			this.inputStream.close();
			this.outputStream.close();
		    this.socket.close();
		}
		catch (Exception e) 
		{
			System.out.println("Couldn't close Connection.");
		}
    }
	
	/**
	 * This method is executed by the garbage collector.
	 */
	protected final void finalize() throws Throwable
	{
		this.close();
	}

	/**
	 * This method is run by the Thread in the Server.
	 */
	@Override
	public final void run()
	{
		// Use a buffered reader to read the type of request.
		this.br = new BufferedReader(new InputStreamReader(this.inputStream));
		
		while (true) 
		{
			try 
			{ 
				if (this.getSocket().isClosed()) { break; }
				
				this.headerLine = br.readLine(); 
				if (headerLine == null) {
				  	return;
				} else if (headerLine.equals(CRLF) || headerLine.equals("")) {
				   	return;
				}
				
				// Request is always the first word!
				StringTokenizer s = new StringTokenizer(headerLine);
				String requestType = s.nextToken();
				
				System.out.println("Request type: " + requestType);
				    
				// Process request and close
				RequestHandler h = this.getServer().getHandler(requestType);
				h.addLine(headerLine);
				
				//String line;
				//while (!(line = br.readLine()).equals(""))
				//{
				//	h.addLine(line);
				//}
				
				h.setThreadedRequest(this);
				h.handleRequest();
				h.handleResponse();
				
				// if the server is not keeping the connections, break the loop, this causes the sockets/streams to close.
				if (!this.getServer().isHoldingConnections())
				{
					break;
				}
			} 
			catch (RequestNotSupportedException e)
			{
				e.print();
				this.close();
				return;
			}
			catch (SocketException e)
			{
				if (e.getMessage().equals("Connection reset"))
				{
					//System.out.println("Closed conn");
				}
				this.close();
				return;
			}
			catch (IOException e) 
			{ 
				e.printStackTrace();
				System.err.println("IO: Cannot process request." + e.getMessage()); 
				this.close();
				return;
			} 
			catch (SecurityException e) 
			{
				System.err.println("Security: Cannot process request."); 
				e.printStackTrace();
				this.close();
				return;
			} 
			catch (IllegalArgumentException e)
			{
				System.err.println("IllegalArgument: Cannot process request."); 
				e.printStackTrace();
				this.close();
				return;
			}
			catch (Exception e)
			{
				System.err.println("General Exception?");
				e.printStackTrace();
				this.close();
				return;
			}
		}
		this.close();
	}
	
	public final void setServer(SimpleHTTPServer simpleHTTPServer) 
	{
		this.server = simpleHTTPServer;
	}
	
	public final void start()
	{
		this.thread.start();
	}

	public final SimpleHTTPServer getServer() {
		return server;
	}
	
	
	public final String getHeaderLine() {
		return headerLine;
	}
	
	public final InputStream getInputStream() {
		return inputStream;
	}
	
	public final OutputStream getOutputStream() {
		return outputStream;
	}
	
	public final BufferedReader getBufferedReader() {
		return br;
	}
	
	public final Socket getSocket() {
		return socket;
	}
	
	public final Thread getThread() {
		return thread;
	}

}